package controller

import (
	"context"
	"encoding/json"
	"fmt"
	"time"
	"v8s/logger"
	"v8s/metrics"

	"go.uber.org/zap"

	"gopkg.in/yaml.v3"

	"v8s-api/rest-api/target/generated-sources"
	"v8s-ssp-proxy/internal/types"
	"v8s-ssp-proxy/internal/writer"
)

type (
	Handler struct {
		log *zap.SugaredLogger

		writer    *writer.Writer
		v8sClient *generated.ClientWithResponses
	}
)

func NewHandlers(writer *writer.Writer, v8sClient *generated.ClientWithResponses) *Handler {
	return &Handler{
		log: logger.Logger().Named("handlers").Sugar(),

		writer:    writer,
		v8sClient: v8sClient,
	}
}

func (h *Handler) CreateTask(ctx context.Context, stepChan chan types.Step, resourceID string, resourceCode string, composeJSON1 string, projectCode string, resourceName string, taskName string) error {
	return metrics.WithOperation("order-task", "v8s", "ssp-proxy", func() error {
		stepChan <- types.Step{Name: stepINIT, Status: []string{"STARTED", "IN_PROGRESS", "COMPLETE"}}

		stepChan <- types.Step{Name: stepCreateTask, Status: []string{"STARTED", "IN_PROGRESS"}}

		composeYAML := map[string]interface{}{}
		err := yaml.Unmarshal([]byte(composeJSON1), &composeYAML)
		if err != nil {
			return fmt.Errorf("failed to unmarshall compose from yaml: %w", err)
		}

		composeJSON, err := json.Marshal(composeYAML)
		if err != nil {
			return fmt.Errorf("failed to marshalled to json: %w", err)
		}

		resourceName2 := generated.ResourceName(resourceName)
		projectName := generated.ProjectId(projectCode)

		request := generated.CreateTaskJSONRequestBody{
			ResourceName: resourceName2,
			TaskName:     generated.TaskName(taskName),
			Project:      projectName,
			Compose:      generated.Compose(composeJSON),
			Metadata: &map[string]interface{}{
				"ssp-v8s": resourceID,
			},
		}

		response, err := h.v8sClient.CreateTaskWithResponse(ctx, request)
		if err != nil {
			return fmt.Errorf("failed to create task in project '%s': %w", projectCode, err)
		}

		if response.StatusCode() != 200 {
			return fmt.Errorf("failed to create task in project '%s', code '%s', body '%s'", projectCode, response.Status(), string(response.Body))
		}

		task, err := h.checkReadiness(ctx, taskReadyStatus, resourceName)
		if err != nil {
			stepChan <- types.Step{Name: stepCreateTask, Error: true, Content: map[string]string{"error": err.Error()}}
			return err
		}

		if len(task.Endpoints) > 0 {
			err = h.sendAttributes(resourceID, types.InstanceAttributesSecurity{
				Endpoints: endpointsMarkdown(task),
			})
			if err != nil {
				stepChan <- types.Step{Name: stepCreateTask, Error: true, Content: map[string]string{"error": err.Error()}}
				return err
			}
		}

		stepChan <- types.Step{Name: stepCreateTask, Status: []string{"COMPLETE"}}

		return nil
	})
}

func (h *Handler) DeleteTask(ctx context.Context, stepChan chan types.Step, projectCode string, resourceCode string) error {
	return metrics.WithOperation("delete-task", "v8s", "ssp-proxy", func() error {
		stepChan <- types.Step{Name: stepRemoveService, Status: []string{"STARTED", "IN_PROGRESS"}}

		resourceName := generated.ResourceName(resourceCode)

		response, err := h.v8sClient.DeleteTaskWithResponse(ctx, resourceName)
		if err != nil {
			return fmt.Errorf("failed to delete task '%s': %w", resourceCode, err)
		}

		if response.StatusCode() == 404 {
			stepChan <- types.Step{Name: stepRemoveService, Status: []string{"COMPLETE"}}
			return nil
		}

		if response.StatusCode() >= 200 && response.StatusCode() < 300 {
			stepChan <- types.Step{Name: stepRemoveService, Status: []string{"COMPLETE"}}
			return nil
		}

		stepChan <- types.Step{Name: stepStopping, Error: true, Content: map[string]string{"error": fmt.Sprintf("failed to delete task '%s'", resourceCode)}}
		return fmt.Errorf("failed to delete task '%s'", resourceCode)
	})
}

func (h *Handler) StopTask(ctx context.Context, stepChan chan types.Step, projectCode string, resourceCode string) error {
	return metrics.WithOperation("stop-task", "v8s", "ssp-proxy", func() error {
		stepChan <- types.Step{Name: stepStopping, Status: []string{"STARTED", "IN_PROGRESS"}}

		resourceName := generated.ResourceName(resourceCode)
		_, err := h.v8sClient.SuspendTaskWithResponse(ctx, resourceName)
		if err != nil {
			return fmt.Errorf("cannot suspend resource '%s': %w", resourceName, err)
		}

		_, err = h.checkReadiness(ctx, taskPausedStatus, resourceCode)
		if err != nil {
			stepChan <- types.Step{Name: stepStopping, Error: true, Content: map[string]string{"error": err.Error()}}
			return err
		}

		stepChan <- types.Step{Name: stepStopping, Status: []string{"COMPLETE"}}

		return nil
	})
}

func (h *Handler) ResumeTask(ctx context.Context, stepChan chan types.Step, projectCode string, resourceCode string) error {
	return metrics.WithOperation("resume-task", "v8s", "ssp-proxy", func() error {
		stepChan <- types.Step{Name: stepResume, Status: []string{"STARTED", "IN_PROGRESS"}}

		resourceName := generated.ResourceName(resourceCode)
		_, err := h.v8sClient.ResumeTaskWithResponse(ctx, resourceName)
		if err != nil {
			return fmt.Errorf("cannot unsuspend resource '%s': %w", resourceName, err)
		}

		task, err := h.checkReadiness(ctx, taskReadyStatus, resourceCode)
		if err != nil {
			stepChan <- types.Step{Name: stepResume, Error: true, Content: map[string]string{"error": err.Error()}}
			return err
		}

		if len(task.Endpoints) > 0 {
			err = h.sendAttributes(resourceCode, types.InstanceAttributesSecurity{
				Endpoints: endpointsMarkdown(task),
			})
			if err != nil {
				stepChan <- types.Step{Name: stepCreateTask, Error: true, Content: map[string]string{"error": err.Error()}}
				return err
			}
		}

		stepChan <- types.Step{Name: stepResume, Status: []string{"COMPLETE"}}

		return nil
	})
}

func (h *Handler) GetTask(ctx context.Context, resourceID string) (types.Task, error) {
	return metrics.WithSupplier[types.Task]("get-task", "v8s", "ssp-proxy", func() (types.Task, error) {
		resourceName := generated.ResourceName(resourceID)
		response, err := h.v8sClient.GetTaskWithResponse(ctx, resourceName)
		if err != nil {
			return types.Task{}, fmt.Errorf("cannot get status of task '%s': %w", resourceName, err)
		}

		if response.StatusCode() != 200 {
			return types.Task{}, fmt.Errorf("cannot get status of task '%s': status '%s', response '%s'", resourceName, response.Status(), string(response.Body))
		}

		task, err := convert(*response.JSON200)
		if err != nil {
			return types.Task{}, fmt.Errorf("cannot convert response of task '%s': response '%s', err %w", resourceName, string(response.Body), err)
		}

		return task, nil
	})
}

func (h *Handler) checkReadiness(ctx context.Context, wantedState, resourceName string) (types.Task, error) {
	fmt.Println("CheckReadiness")

	t := time.NewTimer(5 * time.Second)
	for {
		select {
		case <-t.C:
			fmt.Println("CheckReadiness timer click")
			resourceID := generated.ResourceName(resourceName)
			response, err := h.v8sClient.GetTaskWithResponse(ctx, resourceID)
			if err != nil {
				return types.Task{}, fmt.Errorf("cannot get status of task '%s': %w", resourceName, err)
			}
			//fmt.Println("CheckReadiness response:", &response)

			if response.StatusCode() != 200 {
				return types.Task{}, fmt.Errorf("cannot get status of task '%s': status '%s', response '%s'", resourceName, response.Status(), string(response.Body))
			}

			task, err := convert(*response.JSON200)
			if err != nil {
				return types.Task{}, fmt.Errorf("cannot convert response of task '%s': response '%s', err %w", resourceName, string(response.Body), err)
			}
			fmt.Println("Check")
			fmt.Println("CheckReadiness task from response:", task)

			if string(task.Status) == wantedState {
				h.log.With("task", task.RN).Debug("instance is ready")
				return task, nil
			}

			//} else if strings.ToLower(instance.Status) == errorState {
			//	log.With("resourceName", resourceName).Warnf("failed to run instance, cluster: %s, resourceName: %s", instance.Cluster, resourceName)
			//	if i == len(tasks[0].Instances)-1 {
			//		return types.TaskResponse{}, fmt.Errorf("failed to run task, all instances down")
			//	}
			//}

			// TODO return right status of the product instance
			// if at least one instance is in the wantedState it means that product is ready for use
			//for i, instance := range task.Task.Instances {
			//	log.Debugf("checking instance readiness: %+v", instance)
			//	if strings.ToLower(instance.Status) == wantedState {
			//		log.With("cluster", instance.Cluster).Debug("instance is ready")
			//		return task, nil
			//	} else if strings.ToLower(instance.Status) == errorState {
			//		log.With("resourceName", resourceName).Warnf("failed to run instance, cluster: %s, resourceName: %s", instance.Cluster, resourceName)
			//		if i == len(tasks[0].Instances)-1 {
			//			return types.TaskResponse{}, fmt.Errorf("failed to run task, all instances down")
			//		}
			//	}
			//}

			t.Reset(5 * time.Second)
		case <-ctx.Done():
			return types.Task{}, fmt.Errorf("timeout exceeded while waiting task ready status, resourceName: %s", resourceName)
		}
	}
}

func (h *Handler) sendAttributes(rqUUID string, security types.InstanceAttributesSecurity) error {
	fmt.Println("SendAttributes")
	content, err := json.Marshal(security)
	if err != nil {
		return fmt.Errorf("marshal instance attributes error: %w", err)
	}

	msg := types.InstanceAttributes{
		ResourceID: rqUUID,
		Content:    string(content),
	}

	b, err := json.Marshal(msg)
	if err != nil {
		return fmt.Errorf("marshal instance attributes error: %w", err)
	}

	h.writer.WriteMsg(rqUUID, resourceStatusHeader, resourceAttributesHeaderValue, b)
	return nil
}
